﻿#ifndef __GEOMETRY_H__
#define __GEOMETRY_H__

#include <iostream>
#include <cmath>
#include <cassert>


template <class t> 
struct Vec2 
{
    union {
        struct {t u, v;};
        struct {t x, y;};
        t raw[2];
    };
    Vec2() : u(0), v(0) {}
    Vec2(t _u, t _v) : u(_u),v(_v) {}
    inline Vec2<t> operator +(const Vec2<t> &V) const { return Vec2<t>(u+V.u, v+V.v); }
    inline Vec2<t> operator -(const Vec2<t> &V) const { return Vec2<t>(u-V.u, v-V.v); }
    inline Vec2<t> operator *(float f)          const { return Vec2<t>(u*f, v*f); }
    inline t&       operator [](const size_t i)  { assert(i<2); return i<=0? x:y;}
    inline const t& operator [](const size_t i) const { assert(i<2); return i<=0? x:y;}
    template <class > friend std::ostream& operator<<(std::ostream& s, Vec2<t>& v);
};

template <class t> 
struct Vec3 
{
    union {
        struct {t x, y, z;};
        struct { t ivert, iuv, inorm; };
        t raw[3];
    };
    Vec3() : x(0), y(0), z(0) {}
    Vec3(t _x, t _y, t _z) : x(_x),y(_y),z(_z) {}
    inline Vec3<t> operator ^(const Vec3<t> &v) const { return Vec3<t>(y*v.z-z*v.y, z*v.x-x*v.z, x*v.y-y*v.x); }
    inline Vec3<t> operator +(const Vec3<t> &v) const { return Vec3<t>(x+v.x, y+v.y, z+v.z); }
    inline Vec3<t> operator -(const Vec3<t> &v) const { return Vec3<t>(x-v.x, y-v.y, z-v.z); }
    inline Vec3<t> operator -() const { return Vec3<t>(x * -1.f, y * -1.f, z * -1.f); }
    inline Vec3<t> operator *(float f)          const { return Vec3<t>(x*f, y*f, z*f); }
    inline t       operator *(const Vec3<t> &v) const { return x*v.x + y*v.y + z*v.z; }
    inline t&       operator [](const size_t i)       { assert(i<3); return i<=0? x:i<=1?y:z;}
    inline const t& operator [](const size_t i) const { assert(i<3); return i<=0? x:i<=1?y:z;}
    float norm () const { return std::sqrt(x*x+y*y+z*z); }
    Vec3<t> & normalize(t l=1) { *this = (*this)*(l/norm()); return *this; }
    template <class > friend std::ostream& operator<<(std::ostream& s, Vec3<t>& v);
};

template<class T>
struct Vec4
{
    Vec4() :x(T()), y(T()), z(T()), w(T())  {}
    Vec4(T X, T Y, T Z, T W) :x(X),y(Y),z(Z),w(W){}
    inline T& operator [](const size_t i) { assert(i < 4); return i <= 0 ? x : (i == 1 ? y : (i == 2 ? z : w)); }
	inline const T& operator [](const size_t i) const { assert(i < 4); return i <= 0 ? x : (i == 1 ? y : (i == 2 ? z : w)); }

    T x, y, z, w;
};

typedef Vec2<float> Vec2f;
typedef Vec2<int>   Vec2i;
typedef Vec3<float> Vec3f;
typedef Vec4<float> Vec4f;
typedef Vec3<int>   Vec3i;

template <class t> std::ostream& operator<<(std::ostream& s, Vec2<t>& v) {
    s << "(" << v.x << ", " << v.y << ")\n";
    return s;
}

template <class t> std::ostream& operator<<(std::ostream& s, Vec3<t>& v) {
    s << "(" << v.x << ", " << v.y << ", " << v.z << ")\n";
    return s;
}

#endif //__GEOMETRY_H__